﻿module Sustainalytics.Routing {

    import Claims = Sustainalytics.Claims;

    export class RoutingService implements IRoutingService {

        constructor(
            private $rootScope: ng.IScope,
            private $q: ng.IQService,
            private ClaimsAuthorizationService: Claims.IClaimsAuthorizationService,
            private $injector: ng.auto.IInjectorService,
            public routeInfoMap: RouteInfoMap,
            private routingEngineAdapter: IRoutingEngineAdapter) {
        }

        public getRouteParams(): any {
            var ctx = this;
            return ctx.getRouteEngineAdapter().getRouteParams();
        }

        public getRouteEngineAdapter(): IRoutingEngineAdapter {
            var ctx = this;
            return ctx.routingEngineAdapter;
        }

        public validateRouteSecurity(security: RouteSecurity): ng.IPromise<boolean> {
            var ctx = this;

            var def = ctx.$q.defer();

            // resources
            var isValidResources = true;
            var promiseResources = ctx.validateResources(security).then(function (isValid) {
                isValidResources = isValid;
            });;

            // user function
            var isValidUserFunc = true;
            var promiseUserFunc = ctx.validateUserFunction(security).then(function (isValid) {
                isValidUserFunc = isValid;
            });

            // security services
            var isValidSecurityServiceList = true;
            var promiseSecurityServices = ctx.validateSecurityServices(security).then(function (isValid) {
                isValidSecurityServiceList = isValid;
            });

            ctx.$q.all([promiseResources, promiseUserFunc, promiseSecurityServices]).then(
                // success
                function () {
                    def.resolve(isValidResources && isValidUserFunc && isValidSecurityServiceList);
                },
                // error
                function () {
                    def.reject();
                });

            return def.promise;
        }

        private validateResources(security: RouteSecurity): ng.IPromise<boolean> {
            var ctx = this;

            var def = ctx.$q.defer();

            var valid: boolean = true;

            // check if resources are valid
            if (security && security.resources && security.resources.length > 0) {

                $.each(security.resources, (index, res) => {
                    var resValid = this.ClaimsAuthorizationService.Validate(res.resource, res.verb, res.parameters);

                    // if at least one resource is not valid then logout and redirect to login
                    if (!resValid) {

                        valid = false;
                        return false;
                
                    }
                    return resValid;
                });
            }

            def.resolve(valid);

            return def.promise;
        }

        private validateUserFunction(security: RouteSecurity): ng.IPromise<boolean> {
            var ctx = this;

            // check using user defined security function
            if (security && security.securityFunc) {
                return security.securityFunc(security, ctx.$injector);
            }
            else {
                var def = ctx.$q.defer();
                def.resolve(true);
                return def.promise;
            }
        }

        // validate using security services
        private validateSecurityServices(security: RouteSecurity): ng.IPromise<boolean> {
            var ctx = this;

            var def = ctx.$q.defer();

            if (security && security.securityServices && security.securityServices.length > 0) {

                // create array of service promises
                var securityServicePromises = [];

                var isOverallValid = true;

                security.securityServices.forEach(function (serviceName) {

                    // get security service
                    var service: IRouteSecurityService

                    try {
                        service = ctx.$injector.get(serviceName);
                    }
                    catch (ex) {
                        console.log('RoutingService :: validateSecurityServices() :: Could not validate route security using service \'' + serviceName + '\'.', security, ex);
                        securityServicePromises.push(ctx.$q.when(false));
                        return;
                    }

                    // validate route security using service
                    var validatePromise = service.validate(security);

                    validatePromise.then(
                        // success
                        function (isValid) {
                            isOverallValid = isOverallValid && isValid;
                        },
                        // error
                        function () {
                        });

                    securityServicePromises.push(validatePromise);
                });

                ctx.$q.all(securityServicePromises).then(
                    // success
                    function () {
                        def.resolve(isOverallValid);
                    },
                    // error
                    function () {
                        def.reject();
                    });
            }
            else {
                def.resolve(true);
            }

            return def.promise;
        }

        public isDefaultRoute(path: string): boolean {
            return path === '/';
        }
    }

    export var routingServiceName = 'RoutingService';

    //angular.module(routingModuleName).service(routingServiceName,
    //    ['$rootScope', '$q', 'ClaimsAuthorizationService', '$injector', RoutingService]);
} 